/*
 Copyright (C) 2008-2009  Santiago Carot Nemesio
 email: scarot@libresoft.es

 This program is a (FLOS) free libre and open source implementation
 of a multiplatform manager device written in java according to the
 ISO/IEEE 11073-20601. Manager application is designed to work in 
 DalvikVM over android platform.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */
package ieee_11073.part_20601.fsm.manager;

import es.libresoft.openhealth.chap.ManagerAuthenticator;
import es.libresoft.openhealth.events.Event;
import es.libresoft.openhealth.utils.IFIFO;
import es.libresoft.openhealth.utils.IUnlock;
import ieee_11073.part_20601.asn1.ApduType;
import ieee_11073.part_20601.fsm.State;
import ieee_11073.part_20601.fsm.StateController;
import ieee_11073.part_20601.fsm.StateHandler;
import ieee_11073.part_20601.phd.channel.InitializedException;
import ieee_11073.part_20601.phd.dim.MDS;
import java.util.concurrent.Semaphore;

public class ManagerStateController implements StateController {

    private State state;
    private Semaphore semInputs = new Semaphore(0);
    private Semaphore semEvents = new Semaphore(0);
    private IFIFO<ApduType> inputQueue;
    private IFIFO<ApduType> outputQueue;
    private IFIFO<Event> eventQueue;
    private DispatcherApduThread dispatcher;
    private DispatcherEventThread dispatcherEvents;
    private MDS mds;
    private boolean initialized = false;
    private final ManagerAuthenticator authenticator;
    private StateHandler state_handler = new StateHandler() {
        @Override
        public void changeState(State newState) {
            state = newState;
        }
        
        @Override
        public ManagerAuthenticator getAuthenticator() {
            return authenticator;
        }

        @Override
        public MDS getMDS() {
            return mds;
        }

        @Override
        public void send(ApduType apdu) {
            outputQueue.add(apdu);
        }

        @Override
        public void setMDS(MDS newMds) {
            mds = newMds;
        }
    };
    private IUnlock dispatcherController = new IUnlock() {
        @Override
        public void unlock() {
            semInputs.release();
        }
    };
    private IUnlock eventController = new IUnlock() {
        @Override
        public void unlock() {
            semEvents.release();
        }
    };

    public ManagerStateController(ManagerAuthenticator authenticator) {
        this.authenticator = authenticator;
        this.state = new MDisconnected(state_handler);
    }

    public void configureController(IFIFO<ApduType> inputQueue, IFIFO<ApduType> outputQueue, IFIFO<Event> eventQueue) {
        //dev_handler = handler;
        this.eventQueue = eventQueue;
        this.eventQueue.setHandler(eventController);
        this.inputQueue = inputQueue;
        this.inputQueue.setHandler(dispatcherController);
        this.outputQueue = outputQueue;
    }

    public void initFSMController() throws InitializedException {
        if (!initialized) {
            dispatcher = new DispatcherApduThread();
            dispatcher.start();
            dispatcherEvents = new DispatcherEventThread();
            dispatcherEvents.start();
            initialized = true;
        } else {
            throw new InitializedException("Manager state controller is already initialized.");
        }
    }

    @Override
    public void processApdu(ApduType apdu) {
        inputQueue.add(apdu);
    }

    @Override
    public void processEvent(Event event) {
        eventQueue.add(event);
    }

    /**
     * Receiver thread for the input channel
     *
     * @author sancane
     */
    public class DispatcherApduThread extends Thread {

        @Override
        public void run() {
            boolean repeat = true;
            while (repeat) {
                try {
                    semInputs.acquire();
                    //Send input ADPU to finite state machine
                    state.process(inputQueue.remove());
                } catch (InterruptedException e1) {
                    System.out.println("Interrupted dispatcher Apdu thread");
                    repeat = false;
                } catch (Exception e) {
                    System.out.println("Exception dispatcher Apdu thread");
                    repeat = false;
                    e.printStackTrace();
                }
            }
            System.out.println("Exiting dispatcher");
        }
    }

    /**
     * Receiver thread for events input channel
     *
     * @author sancane
     */
    public class DispatcherEventThread extends Thread {

        @Override
        public void run() {
            boolean repeat = true;
            while (repeat) {
                try {
                    semEvents.acquire();
                    //Send input Event to finite state machine
                    state.processEvent(eventQueue.remove());
                } catch (InterruptedException e1) {
                    System.out.println("Interrupted dispatcher Events thread");
                    repeat = false;
                } catch (Exception e) {
                    System.out.println("Exception dispatcher Events thread");
                    repeat = false;
                    e.printStackTrace();
                }
            }
            System.out.println("Exiting dispatcher event");
        }
    }
}
